# -*- coding: utf-8 -*-

'''
Created on 26/11/2011

@author: Luis M. Ugalde
www.forondarena.net

Copyright © 2011 Luis M. Ugalde
Licensed under the terms of the MIT License
(see LICENSE file for details)

This software can't be considered to be even in Alpha state. It still needs a lot of work.

Remember that abstract interfaces have a different syntax in python 3:
 You need the: from abc...
                class AbstractReader(metaclass = ABCMeta)
 and you dont need the __metaclass__ ...
'''


from abc import abstractmethod, ABCMeta
import zmq
from statestores.statestoreexception import StateStoreException

class AbstractStateStore(object):
    '''
    Generic class to be inherited by every state store implementation.
    StateStores must implement all defined abstract methods. Raise TypeError otherwise
    '''


    __metaclass__ = ABCMeta


    def __init__(self, *args, **kwargs):
        self.context = zmq.Context()
        self.poller = zmq.Poller()
        self.parserconnectaddresses = []
        self.parserconnectsockets = []
        self.parserconnectnotifyaddresses = []
        self.parserconnectnotifysocket = None
        

    def setSource(self, proto = "inproc", host = "parser", port = None):
        '''
        The source for a state store is always a zeromq socket created by a parser.
        '''

        port = str(port)
        self.parserconnectaddresses.append(proto + '://' + host + ':' + port if port else proto + '://' + host)
        print "{}: Parser socket is {}".format(self.__class__.__name__, proto + '://' + host + ':' + port if port else proto + '://' + host)


    def connectSources(self):
        '''
        Connect the previously defined source to receive data from parsers.
        '''

        try:
            i = 0
            while i < len(self.parserconnectaddresses):
                print "Connecting  to {}.".format(self.parserconnectaddresses[i])
                tmpsocket = self.context.socket(zmq.PULL)
                tmpsocket.connect(self.parserconnectaddresses[i])
                self.parserconnectsockets.append(tmpsocket)
                self.poller.register(self.parserconnectsockets[i], zmq.POLLIN)
                i += 1
        except zmq.ZMQError:
            if __debug__: print "Could not connect to parser source sockets. Trying to close everything."
            self.closeSources()
            raise StateStoreException('Could not connect to parser sources.')


    def closeSources(self):
        '''
        Close the open parser socket.
        '''
        
        for i in self.parserconnectsockets:
            self.poller.unregister(i)
            i.setsockopt(zmq.LINGER, 0)
            i.close()


    def setNotifySource(self, proto = "inproc", host = "notifyparser", port = None):
        '''
        Set up a socket to receive broadcast from available parsers.
        '''

        port = str(port)
        self.parserconnectnotifyaddresses.append(proto + '://' + host + ':' + port if port else proto + '://' + host)
        print "{}: Parser notify socket is {}".format(self.__class__.__name__, proto + '://' + host + ':' + port if port else proto + '://' + host)
        
        
    def connectNotifySources(self):
        '''
        Connect the previously defined sources to receive broadcasts from readers.
        '''
        
        try:
            for i in self.parserconnectnotifyaddresses:
                print "Connecting  to {}.".format(i)
                self.parserconnectnotifysocket = self.context.socket(zmq.SUB)
                self.parserconnectnotifysocket.connect(i)
        
            self.parserconnectnotifysocket.setsockopt(zmq.SUBSCRIBE, "")
            self.poller.register(self.parserconnectnotifysocket, zmq.POLLIN)
        except zmq.ZMQError:
            if __debug__: print "Could not connect to parser source notify sockets. Trying to close everything."
            self.closeNotifySources()
            raise StateStoreException('Could not connect to parser notify sources.')


    def closeNotifySources(self):
        '''
        Close the open parser socket.
        '''

        self.poller.unregister(self.parserconnectnotifysocket)        
        self.parserconnectnotifysocket.setsockopt(zmq.LINGER, 0)
        self.parserconnectnotifysocket.close()


    def setBindStateStore(self, proto = "inproc", host = "statestore", port = None):
        '''
        Set up a zeromq socket address to send finished records to writers.
        '''
        
        port = str(port)
        self.statestorebindaddress = proto + '://' + host + ':' + port if port else proto + '://' + host
        print "{}: StateStore bind socket is {}".format(self.__class__.__name__, self.statestorebindaddress) 

        
    def bindStateStore(self):
        '''
        Bind the defined zeromq socket.
        '''
        
        try:
            self.statestorebindsocket = self.context.socket(zmq.PUSH)
            self.statestorebindsocket.bind(self.statestorebindaddress)
        except zmq.ZMQError:
            tmperror = "Unable to bind " + self.statestorebindaddress
            raise StateStoreException(tmperror)



    def closeBindStateStore(self):
        '''
        Close the defined zeromq socket.
        '''

        self.statestorebindsocket.setsockopt(zmq.LINGER, 0)
        self.statestorebindsocket.close()


    def setBindNotifyStateStore(self, proto = "inproc", host = "notifystatestore", port = None):
        '''
        Set up a zeromq socket to broadcast notifications to writers.
        '''
        
        port = str(port)
        self.statestorebindnotifyaddress = proto + '://' + host + ':' + port if port else proto + '://' + host
        print "{}: StateStore bind socket is {}".format(self.__class__.__name__, self.statestorebindnotifyaddress) 


    def bindNotifyStateStore(self):
        '''
        Bind the defined zeromq socket.
        '''
        
        try:
            self.statestorebindnotifysocket = self.context.socket(zmq.PUB)
            self.statestorebindnotifysocket.bind(self.statestorebindnotifyaddress)
        except zmq.ZMQError:
            tmperror = "Unable to bind " + self.statestorebindnotifyaddress
            raise StateStoreException(tmperror)


    def closeBindNotifyStateStore(self):
        '''
        Close the defined zeromq socket.
        '''

        self.statestorebindnotifysocket.setsockopt(zmq.LINGER, 0)
        self.statestorebindnotifysocket.close()


    def closeContext(self):
        self.context.term()


    @abstractmethod
    def processLines(self, *args, **kwargs):
        pass


if __name__ == '__main__':
    print "Please, do not run this"
